5장. 결합에도 등급이 있다

출처: 『소프트웨어 설계의 결합 균형』(블라드 코노노프 지음, 제이펍 2026) | 원서: Balancing Coupling in Software Design (Manning) · 입문판·PDF 재구성

코드는 분위기만 — switch·static·namespace 같은 말은 몰라도 됩니다. 표의 '비유'와 '위험'만 봐도 충분해요.

연결(결합)에도 등급이 있다.

같은 "연결"이라도 어떤 건 위험하고 어떤 건 안전하다.

1960~70년대에 사람들이 이걸 처음으로 6단계로 줄 세웠다.

이 장은 그 6단계를 위에서 아래로 훑는다.


0. 이 장의 새 단어 (3개)

0장에 없던 말은 딱 3개다.

나머지 어려운 말(결합·상위/하위·공유 지식·캡슐화 경계·인터페이스·변경 전파·복잡한 상호작용)은 전부 0장에 있다.

막히면 0장으로 돌아가면 된다.


모듈 결합 6수준 (Module Coupling Levels)

한 문장 뜻 — 두 부품이 어떤 방식으로 연결됐는지를 위험한 순서대로 6칸으로 줄 세운 것.

일상비유 — 매운맛 단계. 같은 "매운 라면"이라도 1단계부터 6단계까지 있다. 등급을 알면 "이건 너무 맵다"를 미리 안다.

한 줄 예 —

var levels = ["콘텐츠", "공통", "외부", "제어", "스탬프", "데이터"]; // 이 값이나 객체를 알게 되는 순간 결합 지점이 됩니다.
// 왼쪽=위험(강함), 오른쪽=안전(약함)

전역 상태 (Global State)

한 문장 뜻 — 여러 부품이 동시에 읽고 쓸 수 있는 공용 저장 공간. 전역 변수·공유 파일·공유 DB가 다 여기 속한다.

일상비유 — 아파트 공동 게시판. 누구나 메모를 쓰고 지운다. 누가 언제 바꿨는지 아무도 모른다.

한 줄 예 —

shared["balance"] = 100; // 어느 부품이든 이 값을 만질 수 있음(전역)

DTO (Data Transfer Object)

한 문장 뜻 — 바깥에 넘겨주려고 일부러 따로 만든 단순 데이터 꾸러미. 속사정을 숨기는 포장지다.

일상비유 — 택배 송장. 집 안 가구 배치는 안 적고, 배달에 꼭 필요한 주소·이름만 적는다.

한 줄 예 —

public class CustomerSnapshot // 이 타입은 예제에서 책임과 결합의 경계를 보여 줍니다.
{
    public string Name { get; init; } // 외부에 보여 줄 읽기용 값입니다.
}

(귀납 도입) 이런 적 있죠?

함수 하나 살짝 고쳤을 뿐인데 엉뚱한 모듈이 터진 적 있죠?

그런데 어떤 연결은 고쳐도 멀쩡하고, 어떤 연결은 건드리기만 해도 와르르 무너진다.

같은 "연결"인데 왜 이렇게 다를까?

옛날 사람들도 똑같이 궁금했다.

그래서 1969년에 '소프트웨어 위기'라는 말까지 나왔다.

"프로그램이 너무 얽혀서 만들기도 고치기도 비싸다"는 한탄이었다.

반세기가 지난 지금도 우리는 같은 고민을 한다.

래리 콘스탄틴과 에드워드 요던은 이 문제를 풀려고 연결을 등급으로 나눴다.

아래 코드를 보자.

// 한 부품이 다른 부품의 "숨겨둔 방"을 직접 연다
payment.ValidateInternally(card); // _ = 비공개 = 열지 말라는 표시

이건 가장 위험한 1단계다.

반대로 아래는 가장 안전한 6단계다.

var status = repo.GetStatus(id); // 필요한 값 하나만 딱 받음

둘 다 "연결"이지만 위험도가 하늘과 땅이다.

이 위험도 줄 세우기가 바로 모듈 결합 6수준이다.


이 장에서 딱 6가지만

이 장에서 딱 6가지만 — 위험한 순서로 외운다.

  1. 콘텐츠 — 벽 뚫고 들어옴 (가장 위험)
  2. 공통 — 공동 게시판 통째로 공유
  3. 외부 — 공동 우편함 한 칸만 공유
  4. 제어 — 조리법까지 지시
  5. 스탬프 — 명함 통째로 건넴
  6. 데이터 — 이름 한 단어만 (가장 안전)

위로 갈수록 서로 많이 알고(공유 지식↑), 변경이 멀리 번진다. 아래로 갈수록 서로 적게 알고, 변경이 번진다. 기본값은 항상 아래쪽(데이터 결합)이다.


개념 1. 콘텐츠 결합 — 벽 뚫고 들어옴 (가장 위험)

망가지는 장면

옆집 사람이 벨도 안 누르고 내 집 벽을 뚫고 들어와 서랍에서 물건을 꺼내 간다.

내가 서랍 위치만 바꿔도 옆집 살림이 깨진다.

게다가 나는 그 사람이 들어온 줄도 모른다.

일상비유

남의 집 벽을 뚫는 행위.

문(공개 인터페이스)으로 안 들어오고 벽(비공개 속사정)을 직접 뚫는다.

비유 코드 위험
벽 뚫고 들어옴 payment._validate(card) 숨긴 걸 바꾸면 침입자가 깨짐
문으로 들어옴(정상) payment.Charge(card) 안전 — 약속된 입구만 씀

정의 — 하위 부품이 상위 부품의 공개 인터페이스를 무시하고 비공개 속사정을 직접 건드리는 것.

다른 이름은 병리학적 결합. 나쁠 수밖에 없어서 붙은 이름이다.

예시 1 (worked — 완성예): 비공개 메서드 강제 호출

// InvoiceGenerator 가 일부러 숨겨둔(_verify_input) 메서드를
// 바깥에서 억지로 끄집어내 호출한다
var invoice = new InvoiceGenerator(); // 이 값이나 객체를 알게 되는 순간 결합 지점이 됩니다.
invoice.VerifyInput("input"); // _ = "쓰지 마세요" 표시인데 무시함

_verify_input은 소비자가 부르라고 만든 게 아니다.

이름을 바꾸거나 지우면 이 코드가 조용히 깨진다.

예시 2 (worked): 남의 서비스 DB 직접 접속

// 결제 서비스의 공식 API 를 안 쓰고, 그 서비스의 DB 에 직접 꽂는다
var rows = payment_db.Query("SELECT * FROM cards"); // 남의 속사정에 직접 침입

결제 서비스가 컬럼 이름만 바꿔도 이 코드가 터진다.

결제 서비스 작성자는 누가 자기 DB를 보고 있는지조차 모른다.

예시 3 (부분완성 — 빈칸 채우기)

아래 두 줄 중 콘텐츠 결합은 어느 쪽일까?

var a = report.BuildSqlInternally(filter); // (A)
var b = report.GetSummary(filter); // (B)

정답은 (A).

_ 로 숨긴 비공개 함수를 바깥에서 직접 부르니 벽을 뚫은 것이다.

예시 4 (독립적용): 직접 판단

마이크로서비스 A가 B의 내부 캐시 파일을 몰래 열어 읽는다.

이건 몇 단계일까?

콘텐츠 결합이다.

B의 공식 입구(API)를 무시하고 B의 속사정(캐시 파일)에 직접 손댔으니까.

미니 시나리오

"DB만 살짝 바꿨는데 왜 옆 팀 서비스가 죽지?"

→ 그 팀이 내 DB에 직접 꽂아 쓰고 있었던 것.

→ 콘텐츠 결합의 전형. 공식 API로만 주고받게 바꿔야 한다.

단순 규칙 — 남이 숨긴 것(_·private·내부 DB·내부 파일)을 직접 건드리면 콘텐츠 결합. 무조건 피한다.


개념 2. 공통 결합 — 공동 게시판 통째로

망가지는 장면

30가구가 공동 게시판 하나에 자유롭게 메모를 쓰고 지운다.

누가 어떤 메모를 바꿨는지 추적이 안 된다.

한 집이 게시판 양식을 바꾸면 30가구가 다 같이 헷갈린다.

일상비유

아파트 공동 게시판.

모두가 같은 판을 읽고 쓴다. 그게 전역 상태다.

비유 코드 위험
공동 게시판 shared["x"] = 1 누가 바꿨는지 추적 불가
내 메모장(격리) local_x = 1 안전 — 나만 만짐

정의 — 여러 부품이 전역적으로 공유되는 데이터 전체를 다 같이 읽고 쓰는 것.

예시 1 (worked): 포트란 COMMON 블록 (역사 원형)

포트란에는 COMMON이라는 명령이 있었다.

여러 서브루틴이 변수 4개(ALPHA·BETA·GAMMA·DELTA)를 통째로 공유했다.

// 포트란 COMMON 을 C# 느낌으로
var shared = {"alpha": 0, "beta": 0, "gamma": 0, "delta": 0}; // 이 값이나 객체를 알게 되는 순간 결합 지점이 됩니다.

void Sub1() { shared["alpha"] += 1; } // beta 만 쓰고 싶어도
void Sub2() { shared["beta"]  += 1; } // 4개 블록 전체를 알아야 함

beta 하나만 쓰고 싶어도 블록 전체를 선언해야 한다.

beta의 자료형을 바꾸면 셋 다 동시에 고쳐야 한다.

예시 2 (worked): 현대판 — 공유 파일/공유 키

// 여러 모듈이 같은 S3 파일을 동시에 읽고 쓴다
write_s3("data.Json", payload); // 모듈 A
var data = read_s3("data.Json"); // 모듈 B — 같은 파일

S3·구글 클라우드 스토리지·애저 스토리지의 같은 파일, 또는 같은 Redis 키를 여러 모듈이 만지는 게 전형이다.

예시 3 (부분완성): 같은 클래스 안 멤버 공유

같은 클래스 안 여러 메서드가 같은 멤버 변수를 만지는 것도 공통 결합일까?

public class Cart // 이 타입은 예제에서 책임과 결합의 경계를 보여 줍니다.
{
    public void Add() { this.Total += 1; } // 같은 Total 필드를
    public void Remove() { this.Total -= 1; } // 둘 다 만짐
}

맞다 — 이것도 공통 결합이다.

다만 자연스러운 경우라 위험이 작다.

"언제 받아들일 만한가"는 더 깊은 주제다(지금은 몰라도 된다).

예시 4 (독립적용)

두 서비스가 같은 Redis 키 session:123을 둘 다 읽고 쓴다.

몇 단계?

공통 결합이다. 전역 저장소(Redis)를 통째로 공유하니까.

왜 위험한가 (짧게)

  • 필요 없는 것까지 다 알아야 함 → 통합 계약이 흐릿하다.
  • 누가 언제 바꿨는지 추적 불가 → 부작용 출처를 못 찾는다.
  • 검증 로직(예: "잔액은 0 이상")이 모든 모듈에 복사돼야 함.
  • 동시에 쓰면 줄 세우기(직렬화)가 필요 → 느려지고 경쟁 조건 발생.

콘텐츠 결합과 다른 점

공통 결합은 모두가 합의하고 같은 판을 쓴다.

콘텐츠 결합은 상위가 모르거나 동의 안 한 채로 침범당한다.

단순 규칙 — 전역 데이터를 통째로 같이 만지면 공통 결합. 기본적으로 줄인다.


개념 3. 외부 결합 — 공동 우편함 한 칸만

망가지는 장면

공동 게시판이 너무 잡다하다.

그래서 두 집 사이에 우편함 한 칸만 두고 꼭 필요한 쪽지만 주고받기로 한다.

좁아졌지만, 여전히 공유 공간이라 우편함이 망가지면 둘 다 곤란하다.

일상비유

공동 우편함 한 칸.

게시판(공통)보다 좁지만 공유 공간이라는 본질은 같다.

비유 코드 위험
공동 게시판(공통) 변수 4개 전부 공유 너무 많이 공유
우편함 한 칸(외부) Config.name 1개만 공유 좁지만 부작용은 남음

정의 — 공통 결합과 똑같이 전역 데이터로 통신하되, 공유하는 데이터의 양이 줄어든 형태.

예시 1 (worked): PL/I EXTERNAL (역사 원형)

PL/I에는 EXTERNAL이라는 표시가 있었다.

두 프로시저가 변수 A 하나를 같은 메모리에서 공유했다.

// ProcA 와 ProcB 가 같은 A 한 개를 가리킴
var external_A = [0]; // 같은 메모리(리스트로 흉내)
void ProcA() { external_A[0] = 5; } // 이 값이나 객체를 알게 되는 순간 결합 지점이 됩니다.
void ProcB() { Console.WriteLine(external_A[0]); } // 5 가 보임 — 같은 곳을 봄

ProcAA를 바꾸면 ProcBA도 같이 바뀐다.

예시 2 (worked): 현대판 — 전역 정적 변수

public class Config // 이 타입은 예제에서 책임과 결합의 경계를 보여 줍니다.
{
    var name = ""; // 전역으로 공유되는 한 개 값

}
void SetName(string n) { Config.Name = n; } // 한 클래스가 씀
void Greet() { Console.WriteLine($"Hi {Config.Name}"); } // 다른 클래스가 읽음

공유하는 건 name 하나뿐이다.

게시판(공통)보다 좁다.

예시 3 (부분완성): 공통 vs 외부 구분

아래 둘 중 외부 결합은?

var shared_all = {"a":1,"b":2,"c":3,"d":4}; // (A) 통째로 공유
Config.Timezone = "KST"; // (B) 한 값만 공유

(B)가 외부 결합이다.

필요한 한 값만 공유하기 때문.

(A)는 통째로 공유하니 공통 결합.

예시 4 (독립적용)

여러 클래스가 같은 static 카운터 하나를 같이 증가시킨다.

몇 단계?

외부 결합이다. 전역 변수 1개만 공유하니까.

공통과 비교

측면 공통 결합 외부 결합
공유 데이터 양 전부 필요한 것만
통신 방식 전역 메모리 전역 메모리(같음)
동시 수정 위험
부작용 추적 어려움 어려움
등급 더 강함 조금 약함

핵심: 공유 폭은 좁아졌지만, 부작용 추적·동시 수정·검증 중복 문제는 그대로다.

단순 규칙 — 전역 데이터를 일부만 공유해도 여전히 외부 결합. 공통보다 낫지만 전역은 전역이다.


개념 4. 제어 결합 — 조리법까지 지시

망가지는 장면

식당에 "스테이크 주세요"가 아니라 "주물팬에 센 불 2분, 약불 5분, 양파 빼고"라고 조리법까지 시킨다.

식당이 조리법을 바꾸려면 나한테 일일이 전화해야 한다.

일상비유

주방에 어떻게 만들지까지 지시.

"무엇"만 시키면 되는데 "어떻게"까지 끼어든다.

비유 코드 위험
조리법까지 지시 send(type="sms", msg) 내부 분기 바꾸면 호출자도 깨짐
메뉴만 주문 notify_user(user, msg) 안전 — 의도만 전달

정의 — 한 부품이 플래그·명령·옵션을 넘겨 다른 부품의 내부 실행 분기를 직접 조종하는 것.

type·mode·flag·action 같은 분기 키워드가 신호다.

예시 1 (worked): 잘못된 예 (before)

static dynamic SendNotification(dynamic type, string message) // 이 메서드의 입력과 출력이 공개 약속입니다.
{
    if   type == "sms":   send_sms(message); // 이 호출은 두 부품 사이의 연결을 보여 줍니다.
    elif type == "email": send_email(message); // 이 호출은 두 부품 사이의 연결을 보여 줍니다.
    elif type == "push":  send_push(message); // 호출자가 이 3개를 다 알아야 함

}
static dynamic NotifyUser(dynamic user, string message) // 이 메서드의 입력과 출력이 공개 약속입니다.
{
    var t = "sms" if user.WantsSms else "email"; // 이 값이나 객체를 알게 되는 순간 결합 지점이 됩니다.
    send_notification(t, message); // 내부 분기 키워드를 호출자가 들고 있음
}

send_notificationpush를 빼면, push를 넘기던 모든 호출자가 런타임 에러.

호출자가 내부 분기 종류를 알아야 한다는 것 자체가 강한 결합이다.

예시 2 (worked): 올바른 예 (after) — 의도만 노출

static dynamic NotifyUser(dynamic user, string message) // 이 메서드의 입력과 출력이 공개 약속입니다.
{
    var channel = pick_channel(user); // 채널 선택은 내부에서 — 바깥에 안 보임
    channel.Send(message); // 이 호출은 두 부품 사이의 연결을 보여 줍니다.

}
notify_user(user, "안녕"); // 호출자는 분기 키워드를 모름

호출자는 "사용자에게 알려"라는 의도만 안다.

채널이 바뀌어도 호출자는 손댈 게 없다.

예시 3 (부분완성): 제어 결합 찾기

process(item, "discount"); // (A)
apply_discount(item); // (B)

(A)가 제어 결합이다.

문자열 "discount"가 내부 분기를 결정하니까.

(B)는 의도만 전달 — 더 낮은 등급.

예시 4 (독립적용)

render(page, format="pdf")에서 format을 바꾸면 호출자도 다 고쳐야 한다.

몇 단계?

제어 결합이다. format 플래그가 내부 분기를 조종하니까.

제어 결합의 신호

  • 인수에 type·mode·flag·action 같은 분기 키워드가 보임.
  • 호출자가 내부 case를 알고 호출해야 함.
  • case를 추가·삭제하면 호출 코드까지 같이 바꿔야 함.

외부 결합과 비교

제어 결합은 전역 상태 대신 명시적 인수로 통신한다 → 외부 결합보다 한 단계 낮다.

하지만 상위가 기능을 완전히 못 숨긴다는 점에서 여전히 강한 편이다.

단순 규칙 — 호출자가 "어떻게 할지"(내부 분기)를 알아야 하면 제어 결합. 의도만 받게 바꾼다.


개념 5. 스탬프 결합 — 명함 통째로 건넴

망가지는 장면

"이름만 알려 줘"라 했는데 명함 한 장을 통째로 준다.

이름·전화·이메일·주소·회사·직책이 다 적혀 있다.

나는 이름만 보지만, 명함 양식이 바뀌면 명함 받은 모든 사람을 신경 써야 한다.

일상비유

명함 한 장 통째.

이름 한 줄 필요한데 전부 다 받는다.

비유 코드 위험
명함 통째 get(id).status 객체 양식 바꾸면 다 영향
이름만 받음 get_status(id) 안전 — 필요한 값만

정의 — 한두 필드만 쓰려는데 상위가 수십 개 필드를 가진 객체 전체를 넘기는 것.

예시 1 (worked): 잘못된 예 (before)

var customer = repo.Get(customer_id); // Customer = 수백 개 필드
var status = customer.Status; // 정작 status 하나만 씀

Customer에 필드를 추가하거나 타입을 바꾸면 모든 소비자가 잠재적으로 영향받는다.

CRM 작성자는 "혹시 누가 이 필드 쓰나" 늘 걱정해야 한다.

예시 2 (worked): 같은 위험, 다른 모습

// 화면이 "고객 이름"만 필요한데 고객 객체 전체를 넘겨받음
static dynamic Render(dynamic customer) // 이 메서드의 입력과 출력이 공개 약속입니다.
{
    Console.WriteLine(customer.Name); // name 하나만 씀
}

화면은 name만 보지만 객체 전체에 묶여 있다.

예시 3 (부분완성): 스탬프 vs 데이터 구분

draw(get_user(id)); // (A) 유저 객체 통째
draw(get_user(id).Avatar); // (B) avatar 하나만

(A)가 스탬프 결합 — 통째로 넘김.

(B)는 필요한 값만 → 더 낮은 데이터 결합.

예시 4 (독립적용)

리포트 함수가 50개 필드짜리 Order 객체를 받아 total 하나만 쓴다.

몇 단계?

스탬프 결합이다.

공통 결합과 다른 점

스탬프 결합은 전역으로 수정 가능한 게 아니라 메서드 호출로 데이터를 받는다.

비즈니스 로직 공유 없음, 동시성 문제 없음.

단지 경계가 헐겁다(필요 없는 걸 너무 많이 받는다)는 게 문제다.

제어 결합과 다른 점

제어 결합은 기능·로직에 대한 지식을 공유한다.

스탬프 결합은 데이터 구조에 대한 지식을 공유한다.

데이터 구조 지식이 행동 지식보다 더 안정적이라 스탬프가 한 단계 더 낮다.

단순 규칙 — 큰 객체를 받아 일부만 쓰면 스탬프 결합. 필요한 값만 받게 줄인다.


개념 6. 데이터 결합 — 이름 한 단어만 (가장 안전)

망가지는 장면

(여긴 망가지지 않는다. 가장 안전한 등급이라서다.)

"이름만 알려 줘"라 하니 이름 한 단어만 적은 메모를 준다.

명함 양식이 바뀌어도 나에겐 영향 없다.

우리 사이엔 "이름 한 단어"라는 작은 약속만 있다.

일상비유

이름 한 단어만 적은 종이.

필요한 것만, 깔끔하게.

비유 코드 위험
이름 한 단어 get_status(id) 거의 안 깨짐
포장지(DTO) get_snapshot(id) 내부 바뀌어도 바깥 안전

정의 — 통합에 꼭 필요한 최소 데이터만 주고받는 것. 비즈니스 로직도, 불필요 필드도 공유하지 않는다.

예시 1 (worked): 필요한 값만 반환 (after)

스탬프 결합 예시를 한 단계 낮춰 보자.

// before(스탬프): 객체 통째 받아 status 만 씀
var status = repo.Get(id).Status; // 이 값이나 객체를 알게 되는 순간 결합 지점이 됩니다.

// after(데이터): status 만 주는 메서드로 분리
var status = repo.GetStatus(id); // 이 값이나 객체를 알게 되는 순간 결합 지점이 됩니다.

Customer 내부 구조가 어떻게 바뀌든, get_status의 반환만 안정적이면 소비자는 멀쩡하다.

예시 2 (worked): DTO로 내부·외부 분리 (극단판)

public class CustomerSnapshot // 이 타입은 예제에서 책임과 결합의 경계를 보여 줍니다.
{
    public string Name { get; init; } // 외부에 보여 줄 읽기용 값입니다.

    @staticmethod; // 이 호출은 두 부품 사이의 연결을 보여 줍니다.
    public dynamic Of(dynamic customer) // 이 메서드의 입력과 출력이 공개 약속입니다.
    {
        return new CustomerSnapshot(customer.Name); // 결과를 호출자에게 돌려줍니다.

}
}
static dynamic Get(dynamic id) // 이 메서드의 입력과 출력이 공개 약속입니다.
{
    return CustomerSnapshot.Of(fetch(id)); // 내부 Customer 가 아니라 포장지를 줌
}

내부 Customer는 마음껏 진화시켜도 된다.

바깥은 CustomerSnapshot이라는 안정된 약속만 본다.

내부(자주 변함)와 외부 약속(잘 안 변함)을 분리한 것.

예시 3 (부분완성): 데이터 결합으로 격상

AnalysisCustomer(30필드)에서 email만 쓴다.

데이터 결합으로 올리려면?

// 빈칸: 필요한 값만 주는 메서드
static dynamic Get(dynamic id) // 이 메서드의 입력과 출력이 공개 약속입니다.
{
    return fetch(id).Email; // 결과를 호출자에게 돌려줍니다.
}

get_email(id)로 분리하면 Analysis는 나머지 29필드를 모른다.

예시 4 (독립적용)

get_total(order_id)가 합계 숫자 하나만 반환한다.

몇 단계?

데이터 결합이다. 가장 안전.

왜 가장 안전한가

  • 공유 지식이 최소 → 변경 전파가 가장 적다.
  • 항상 명시적 공개 인터페이스로만 통합한다.
  • 내부를 바꿔도 바깥 약속이 안 변하면 소비자가 안 깨진다.

단순 규칙 — 새 코드의 기본값은 데이터 결합. 더 강한 결합이 필요하면 이유를 댈 수 있어야 한다.


6수준 한 줄 묶음표

수준 일상 비유 핵심 신호 공유 지식
콘텐츠 (강) 벽 뚫고 들어옴 비공개 침범·남의 DB 직접 접속 구현 세부 전부
공통 공동 게시판 전역 변수·공유 파일·공유 DB 전역 데이터 전부
외부 우편함 한 칸 static 변수·EXTERNAL 전역 데이터 일부
제어 조리법까지 지시 type·mode·flag 인수 내부 로직
스탬프 명함 통째 큰 객체에서 1개만 씀 데이터 구조
데이터 (약) 이름 한 단어 필요한 값만·DTO 최소 데이터

위로 갈수록: 공유 지식↑ · 변경 전파↑ · 캡슐화 깨짐↑

아래로 갈수록: 공유 지식↓ · 인터페이스 명확↑ · 변경 안전↑


5.4 요점 — 코드에서 등급 찾기

지금은 "이 설계가 좋냐 나쁘냐"를 판단하는 단계가 아니다.

경계에서 어떤 종류의 지식이 새 나가는지알아보는 단계다.

코드를 볼 때 세 가지만 물어보면 된다.

  • 공유 DB·공유 파일을 같이 쓰는 부품이 있나? (외부·공통 결합)
  • 데이터 구조를 통째로 주고받는 인터페이스가 있나? (스탬프 대 데이터 결합)
  • 남의 구현 세부에 의존해 통합하는 곳이 있나? (콘텐츠 결합)

이 세 질문으로 코드를 훑으면 등급이 보인다.


정리

  • 같은 "연결"도 등급이 있다. 콘텐츠 → 공통 → 외부 → 제어 → 스탬프 → 데이터 (강 → 약).
  • 위로 갈수록 서로 많이 알고, 변경이 멀리 번진다. 아래로 갈수록 안전하다.
  • 기본값은 가장 안전한 데이터 결합. 더 강한 게 필요하면 이유를 댄다.

한 걸음 더 ▸ (지금 몰라도 됨)

"공통·외부 결합 = 무조건 나쁘다"는 아니다. 같은 클래스 안 메서드들이 멤버 변수를 같이 만지는 건 자연스러운 공통 결합이다. 같은 등급이라도 언제 받아들일 만한지는 더 깊은 주제라, 지금은 "적게 알수록 안전" 규칙 하나만 들고 가면 된다.


더 해보기

분산 시스템에서 콘텐츠 결합은 "남의 서비스 DB 직접 접근"으로 자주 나타난다. 회피책은 Database per Service 패턴 (검증 2026-05-31).


다음 6장 예고 — 또 다른 시대가 만든 결합 모델(공생성)을 본다. (지금 몰라도 됩니다 — 6장에서 천천히 풀려요.)


연습문제

  1. 설명. 결합에도 등급이 있다의 핵심을 처음 듣는 사람에게 한 문장으로 설명하라.
  2. 구분. 두 개념(내용 결합, 데이터 결합)을 실제 예시 하나로 구분하라.
  3. 적용. 내 프로젝트나 학습 노트에서 이 장의 개념을 적용해 작게 개선할 지점을 하나 고르라.

부록 A. 쉬운 용어 사전

한글 용어 원문 영문명 아주 쉬운 뜻 이 장에서 나온 위치
내용 결합 Content Coupling 다른 부품의 내부 구현을 직접 만지는 가장 위험한 결합. 부록 B와 본문 예시
데이터 결합 Data Coupling 필요한 값만 주고받는 비교적 가벼운 결합. 부록 B와 본문 예시
스탬프 결합 Stamp Coupling 필요한 조각보다 큰 데이터 덩어리를 통째로 넘기는 결합. 부록 B와 본문 예시

부록 B. 헷갈리는 개념 비교표

A B 구분 포인트
내용 결합 데이터 결합 내용 결합은 속을 직접 만지고, 데이터 결합은 필요한 값만 주고받는다.
스탬프 결합 데이터 결합 스탬프는 덩어리째 넘기고, 데이터는 필요한 조각만 넘긴다.

부록 C. 더 읽을 자료

  • 이 장의 더 해보기 섹션 — 이미 모아 둔 공식 문서나 실습 링크가 있으면 여기서 먼저 확인한다.
  • 같은 책의 0장 한눈에 보기 — 용어가 막히면 0장의 용어집과 개념 척추로 돌아간다.
  • 원본 딥다이브판 같은 장 — 입문판을 읽고 큰 흐름이 잡힌 뒤 세부 논리를 더 깊게 확인한다.
  • 이 장의 flashcards.json — 읽은 직후 질문만 보고 답을 떠올리는 회상 연습에 쓴다.

부록 D. 연습문제 풀이

  1. 설명 예시. 결합에도 등급이 있다는 변경이 어디로 번지는지 보고, 필요한 연결과 줄여야 할 연결을 구분하게 해 주는 장이다. 중요한 것은 용어를 외우는 것이 아니라, 이 개념이 어떤 입력·부품·결정에 영향을 주는지 말로 풀어 보는 것이다.
  2. 구분 예시. 두 개념(내용 결합, 데이터 결합)의 차이는 이렇게 잡으면 된다. 내용 결합은 속을 직접 만지고, 데이터 결합은 필요한 값만 주고받는다. 실제 사례를 볼 때는 목적, 입력, 실패했을 때의 증상을 따로 적어 보면 헷갈리지 않는다.
  3. 적용 예시. 가장 작은 개선부터 고른다. 예를 들어 이름을 더 분명히 하거나, 평가 기준을 한 줄 추가하거나, 직접 알 필요 없는 내부 정보를 감추는 식으로 시작한다. 한 번에 크게 갈아엎는 것보다 작은 변경 하나를 확인하며 진행하는 쪽이 입문 단계에 맞다.
난이도
에피소드
질문
카드를 로딩 중...
답변

클릭하거나 Space를 눌러 뒤집기

0 / 0
학습 진도 0%
이동   Space 뒤집기   R 셔플